﻿using System;
using System.Globalization;
using System.IO;

namespace FirstFloor.Documents.Pdf
{
    /// <summary>
    /// Provides extension helper for the Token struct.
    /// </summary>
    internal static class TokenExtensions
    {
        /// <summary>
        /// Determines whether the token is of specified type and has specified value.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="type">The type.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        /// 	<c>true</c> if the specified token is match; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsMatch(this Token token, TokenType type, string value)
        {
            return token.Type == type && token.Value == value;
        }

        /// <summary>
        /// Raises an exception if specified token is not of given type.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="type">The type to match.</param>
        public static Token Match(this Token token, TokenType type)
        {
            if (token.Type != type) {
                throw new PdfParseException(Resources.UnexpectedTokenType, token.Type, type);
            }
            return token;
        }

        /// <summary>
        /// Raises an exception if specified token is not of given type and contains given value.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="type">The type.</param>
        /// <param name="value">The value.</param>
        public static Token Match(this Token token, TokenType type, string value)
        {
            token.Match(type);

            if (!token.Value.Equals(value, StringComparison.OrdinalIgnoreCase)) {
                throw new PdfParseException(Resources.UnexceptedTokenValue, token.Value, value);
            }
            return token;
        }

        /// <summary>
        /// Parses a boolean value from specified keyword token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static bool ParseBool(this Token token)
        {
            token.Match(TokenType.Keyword);

            if (token.Value == "true") {
                return true;
            }
            if (token.Value == "false") {
                return false;
            }
            throw new PdfParseException(Resources.InvalidBooleanValue, token.Value);
        }

        /// <summary>
        /// Parses the byte array from given hexadecimal string token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static byte[] ParseHexString(this Token token)
        {
            token.Match(TokenType.HexadecimalString);

            var result = new MemoryStream();
            for (int i = 0; i < token.Value.Length; i += 2) {
                var i1 = ParserUtility.ParseHex(token.Value[i]);
                var i2 = 0;
                if (i + 1 < token.Value.Length) {
                    i2 = ParserUtility.ParseHex(token.Value[i + 1]);
                }

                result.WriteByte((byte)(i1 * 16 + i2));
            }

            return result.ToArray();
        }

        /// <summary>
        /// Parses the integer value from given integer token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static int ParseInteger(this Token token)
        {
            token.Match(TokenType.Number);

            return int.Parse(token.Value, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Parses the double value from given double token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        public static double ParseDouble(this Token token)
        {
            token.Match(TokenType.Number);

            return double.Parse(token.Value, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
        }
    }
}
